use proc_macro2::*;
use quote::*;
use syn::*;

#[cfg(feature = "dts")]
use fomat_macros::*;

mod class;
mod cnst;
mod dts;
mod from;
mod func;
mod into;
mod ty;

use dts::*;

#[derive(Debug)]
pub(crate) enum EnumType {
	Num,
	Str,
	Obj,
	Ref,
	Mut,
	RefExt,
	MutExt,
}

#[derive(Debug)]
pub(crate) enum ClassType {
	Number,
	String,
	Object,
	Wrap,
	External,
}

/// Derive macro for converting Rust objects to N-API objects
#[proc_macro_derive(IntoNpihObj, attributes(napih))]
pub fn into_napih_derive_obj(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	into::expand(&EnumType::Obj, &parse_macro_input!(input)).into()
}

/// Derive macro for converting Rust numeric types to N-API numbers
#[proc_macro_derive(IntoNpihNum, attributes(napih))]
pub fn into_napih_derive_u32(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	into::expand(&EnumType::Num, &parse_macro_input!(input)).into()
}

/// Derive macro for converting Rust string types to N-API strings
#[proc_macro_derive(IntoNpihStr, attributes(napih))]
pub fn into_napih_derive_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	into::expand(&EnumType::Str, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API objects to Rust objects
#[proc_macro_derive(FromNpihObj, attributes(napih))]
pub fn from_napih_derive_obj(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::Obj, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API numbers to Rust numeric types
#[proc_macro_derive(FromNpihNum, attributes(napih))]
pub fn from_napih_derive_u32(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::Num, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API strings to Rust string types
#[proc_macro_derive(FromNpihStr, attributes(napih))]
pub fn from_napih_derive_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::Str, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API references to Rust reference types
#[proc_macro_derive(FromNpihRef, attributes(napih))]
pub fn from_napih_derive_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::Ref, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API mutable references to Rust mutable reference types
#[proc_macro_derive(FromNpihMut, attributes(napih))]
pub fn from_napih_derive_mut(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::Mut, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API external references to Rust reference types
#[proc_macro_derive(FromNpihRefExt, attributes(napih))]
pub fn from_napih_derive_ref_ext(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::RefExt, &parse_macro_input!(input)).into()
}

/// Derive macro for converting N-API external mutable references to Rust mutable reference types
#[proc_macro_derive(FromNpihMutExt, attributes(napih))]
pub fn from_napih_derive_mut_ext(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	from::expand(&EnumType::MutExt, &parse_macro_input!(input)).into()
}

/// Attribute macro for defining N-API functions with automatic type conversion
#[proc_macro_attribute]
pub fn napi_fn(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	func::expand(&parse_macro_input!(attr), &mut parse_macro_input!(input)).into()
}

/// Attribute macro for defining N-API constants
#[proc_macro_attribute]
pub fn napi_const(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	cnst::expand(&parse_macro_input!(attr), &mut parse_macro_input!(input)).into()
}

/// Attribute macro for defining N-API classes with automatic method binding
#[proc_macro_attribute]
pub fn napi_class(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	class::expand(&parse_macro_input!(attr), &mut parse_macro_input!(input)).into()
}

/// Attribute macro for implementing N-API class methods
#[proc_macro_attribute]
pub fn napi_impl(_: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	class::expand_impl(&mut parse_macro_input!(input)).into()
}

/// Attribute macro for defining N-API type definitions with TypeScript declarations
#[proc_macro_attribute]
pub fn napi_type(_: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	ty::expand(&mut parse_macro_input!(input)).into()
}

#[cfg(feature = "dts")]
fn build_dts(dts: DTSResult) -> TokenStream {
	let dts = match dts {
		Ok(v) => v,
		Err(e) => return Error::new(Span::call_site(), e).into_compile_error(),
	};
	let (name, ty, dts, owner) = dts.take();
	if dts.is_empty() {
		return quote! {};
	}
	let name = LitStr::new(&name, Span::call_site());
	let dts = LitStr::new(&dts, Span::call_site());
	let ty = Ident::new(ty, Span::call_site());
	if let Some(owner) = owner {
		quote! {
			const _: () = {
				fn _dts_() -> &'static str {
					::napih::REG_DTS!(#name,#ty,_dts_,#owner);
					#dts
				}
			};
		}
	} else {
		quote! {
			const _: () = {
				fn _dts_() -> &'static str {
					::napih::REG_DTS!(#name,#ty,_dts_);
					#dts
				}
			};
		}
	}
}
